﻿using System;
using System.Collections.Generic;
using System.Text;
using CGLib;
using CGLib.Enums;
using System.Drawing;

namespace CoreGameLib
{
    public enum FieldType
    {
        None,
        Single,
        Double,
        Triple,
        FourKind,
        Sequence,
        Bomb
    }

    public class TienLen : IGame
    {
        private FieldType fType = FieldType.None;
        private GameInfo gInfo;

        private Card lowestCard = null;
        bool started = false;
        bool playerRecentFinished = false;
        int countBeat = 0;
        int playersFinished = 0;

        bool clockwise = true;

        #region Custom Methods

        static bool EqualCardValues(List<Card> list)
        {
            Value chkValue = list[0].Value;

            for (int i = 1; i < list.Count; i++)
            {
                if (list[i].Value != chkValue)
                    return false;
            }

            return true;
        }

        #endregion

        #region Game Info

        public string Name()
        {
            return "Tien Len";
        }

        public IComputer[] Computers()
        {
            return new IComputer[] { new TLAIEasy() };
        }

        public string Description()
        {
            return "Play the popular Vietnamese card game, with the objective of finishing all of your cards before your opponents does.";
        }

        public int MaxPlayers()
        {
            return 4;
        }

        public int MinPlayers()
        {
            return 1;
        }

        public Image ImageOverview()
        {
            return null;
        }

        #endregion

        private int GetCardValue(Value value)
        {
            if (value == Value.Two)
                return 13;
            else if (value == Value.Ace)
                return 12;
            else
                return (int)value - 2;
        }

        private void GameEvents_GameReady(object sender, EventArgs e)
        {
            if (gInfo.MainPlayer.Type == UserType.Host)
                ClientFunc.SendMSNText(gInfo.Players[lowestCard.Player - 1].ID + " " + ((int)lowestCard.Value).ToString() + " " + 
                    ((int)lowestCard.Suite).ToString(), true);
        }

        private void GameEvents_MSNPacketReceived(object sender, MSNPacketEventArgs e)
        {
            if (e.IsCustom)
            {
                lowestCard = new Card((Suite)Convert.ToInt32(e.Packet.Info[2]), (Value)Convert.ToInt32(e.Packet.Info[1]));

                for (int x = 0; x < gInfo.Players.Length; x++)
                {
                    if (gInfo.Players[x].ID == e.Packet.Info[0])
                    {
                        lowestCard.Player = Convert.ToInt32(e.Packet.Info[0]);
                        break;
                    }
                }

                SetStartingPlayer();
            }
        }

        private void GameEvents_TurnEnded(object sender, TurnEndedEventArgs e)
        {
            if (e.Finished)
            {
                ClientFunc.ChangeGameProperty(ClientPropChange.AllowFreeTurn, false);

                playerRecentFinished = true;
                countBeat = playersFinished;
                playersFinished++;
            }

            else if (playerRecentFinished)
            {
                if (!e.Player.SkippedTurn || countBeat == gInfo.Players.Length)
                {
                    ClientFunc.ChangeGameProperty(ClientPropChange.AllowFreeTurn, true);

                    playerRecentFinished = false;
                    countBeat = 0;
                }
                else countBeat++;
            }
        }

        private void GameEvents_TurnChanged(object sender, PlayerEventArgs e)
        {
            if (e.Player.Number == 1)
                ClientFunc.ChangeGameProperty(ClientPropChange.Skippable, !e.Player.FreeTurn);

            if (e.Player.FreeTurn)
                fType = FieldType.None;

            if (!started)
            {
                started = true;
                lowestCard = null;
            }
        }

        private void GameEvents_PlayerCardsReady(object sender, PlayerEventArgs e)
        {
            if (gInfo.Players.Length != 4)
            {
                for (int i = 0; i < 13; i++)
                {
                    if (lowestCard == null ||
                        (e.Player.Cards[i].Suite < lowestCard.Suite && GetCardValue(e.Player.Cards[i].Value) <= GetCardValue(lowestCard.Value)))
                        lowestCard = e.Player.Cards[i];
                }
            }
            else if (lowestCard == null)
            {
                Card card = e.Player.Cards.Find(delegate(Card cardFind) { return cardFind.Suite == Suite.Spade && cardFind.Value == Value.Three; });

                if (card != null)
                    lowestCard = card;
            }

            if (gInfo.Players.Length == e.Player.Number)
                SetStartingPlayer();
        }

        private void SetStartingPlayer()
        {
            ClientFunc.ChangeGameProperty(ClientPropChange.PlayerTurn, lowestCard.Player);
            ClientFunc.ChangeGameProperty(ClientPropChange.Skippable, false);
        }

        public FieldType FieldType
        {
            get { return fType; }
            set { fType = value; }
        }

        public Card LowestCardInGame
        {
            get { return lowestCard; }
            set { lowestCard = value; }
        }

        public bool ValidateCards(List<Card> chosenCards, List<Card> currentCards)
        {
            if (lowestCard != null &&
                chosenCards.Find(delegate(Card card)
                { return card.Value == lowestCard.Value && card.Suite == lowestCard.Suite; }) == null)
                return false;

            // If player has a four of a kind, it can be used to chop a single 2.
            if (fType == FieldType.Single && currentCards.Count == 1 && currentCards[0].Value == Value.Two &&
                chosenCards.Count == 4)
            {
                fType = FieldType.FourKind;
                goto RETTRUE;
            }

            #region Sequence / Bomb

            // Check if combo is sequence or a bomb
            if ((fType == FieldType.None && chosenCards.Count > 2) || 
                ((fType == FieldType.Sequence || fType == FieldType.Bomb) && chosenCards.Count == currentCards.Count) ||
                (currentCards.Count == 1 && currentCards[0].Value == Value.Two))
            {
                bool accept = true;
                bool ischop = chosenCards.Count >= 6 && chosenCards[1].Value - chosenCards[0].Value == 0;
                int increment = ischop ? 2 : 1;

                // Ensure the last card of the bomb / sequence isn't a 2, and the start of the bomb / sequence value is greater
                // than the current one.
                if (currentCards.Count != 0 && (chosenCards[chosenCards.Count - 1].Value == Value.Two || 
                    !(chosenCards[0].Value > currentCards[0].Value)))
                    accept = false;

                for (int i = ischop ? 3 : 1; i < chosenCards.Count; i += increment)
                {
                    if (!accept)
                        break;

                    // Verify if the chop or sequence is valid
                    if (ischop)
                        accept = chosenCards.Count >= 6 && chosenCards[i].Value - chosenCards[i - 1].Value == 0;
                    else
                        accept = Parsers.GetGCV(this, chosenCards[i].Value) - Parsers.GetGCV(this, chosenCards[i - 1].Value) == 1;
                }

                // If the card is a 2, the combo must be a chop and depending on the quantity of 2's,
                // the number of pairs in the sequence must be powerful enough to beat it.
                // (eg. 1 card of 2 = 3 pairs, 2 cards of 2 = 4 pairs etc...)
                if (currentCards.Count != 0 && currentCards[0].Value == Value.Two && ischop)
                    accept = 4 + currentCards.Count * 2 != chosenCards.Count;

                if (accept == true)
                {
                    fType = ischop ? FieldType.Bomb : FieldType.Sequence;
                    goto RETTRUE;
                }
            }

            #endregion

            // Number of players cards must equal to the number of cards on the field, unless
            // the field is empty.
            if (currentCards.Count != 0 && chosenCards.Count != currentCards.Count)
                return false;

            // Check if the players selected cards can beat the cards on the field
            switch (chosenCards.Count)
            {
                case 1:
                    {
                        if (fType == FieldType.None)
                        {
                            fType = FieldType.Single;
                            goto RETTRUE;
                        }

                        if (Parsers.GetGCV(this, chosenCards[0].Value) == Parsers.GetGCV(this, currentCards[0].Value) &&
                            chosenCards[0].Suite > currentCards[0].Suite)
                        {
                            fType = FieldType.Single;
                            goto RETTRUE;
                        }
                        else
                            return (Parsers.GetGCV(this, chosenCards[0].Value) > Parsers.GetGCV(this, currentCards[0].Value));

                    }
                case 2:
                case 3:
                case 4:
                    {
                        if (EqualCardValues(chosenCards))
                        {
                            if (fType == FieldType.None ||
                                Parsers.GetSuiteSum(currentCards) == Parsers.GetSuiteSum(chosenCards) &&
                                Parsers.GetGCV(this, chosenCards[0].Value) > Parsers.GetGCV(this, currentCards[0].Value))
                            {
                                fType = (FieldType)chosenCards.Count;
                                goto RETTRUE;
                            }
                            else
                                return false;
                        }

                        break;
                    }
            }

            return false;

        RETTRUE:
            lowestCard = null;
            return true;
        }

        public void Initialised(GameInfo info)
        {
            gInfo = info;

            GameEvents.PlayerCardsReady += new EventHandler<PlayerEventArgs>(GameEvents_PlayerCardsReady);
            GameEvents.TurnChanged += new EventHandler<PlayerEventArgs>(GameEvents_TurnChanged);
            GameEvents.TurnEnded += new EventHandler<TurnEndedEventArgs>(GameEvents_TurnEnded);

            if (gInfo.ServerType == SvrType.MSN)
            {
                GameEvents.GameReady += new EventHandler(GameEvents_GameReady);
                GameEvents.MSNPacketReceived += new EventHandler<MSNPacketEventArgs>(GameEvents_MSNPacketReceived);
            }

            ClientFunc.ChangeGameProperty(ClientPropChange.ClockwiseTurn, clockwise);
        }

        public void Dispose()
        {
            GameEvents.PlayerCardsReady -= GameEvents_PlayerCardsReady;
            GameEvents.TurnChanged -= GameEvents_TurnChanged;
            GameEvents.TurnEnded -= GameEvents_TurnEnded;

            if (gInfo.ServerType == SvrType.MSN)
            {
                GameEvents.GameReady -= GameEvents_GameReady;
                GameEvents.MSNPacketReceived -= GameEvents_MSNPacketReceived;
            }
        }

        public Value HighestCard()
        {
            return Value.Two;
        }

        public Value LowestCard()
        {
            return Value.Three;
        }

        public void LoadOptions(System.Windows.Forms.Form parentFrm)
        {
            TLOptions options = new TLOptions(clockwise, TLAIEasy.allowCustomNames);

            if (options.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                clockwise = options.chkCW.Checked;
                TLAIEasy.allowCustomNames = options.chkAIName.Checked;
            }
        }

        public bool AllowOptions()
        {
            return true;
        }
    }
}
